#include "processingthread.h"
#include <QMetaObject>
processingThread::processingThread(QTcpSocket *socket, int key)
    : socketIo(nullptr)
    , sourceData("")
    , byteSize(40000)
    , socket(socket)
    , key(key)
    , updatePath("/home/xiaoxin/update/")
{
    socket->setParent(this);
    qDebug() << getDirAllFile(updatePath);
}

void processingThread::init()
{
    socketIo = new Socket(socket, key);

    // QThread* thread = new QThread();
    //socketIo->moveToThread(thread);

    connect(socketIo, SIGNAL(signalSendToThread(QByteArray)),
            this, SLOT(onReadyRead(QByteArray)));

    connect(socketIo, SIGNAL(signalDisconnect(int)),
            this, SLOT(onDisconnect(int)));

    connect(this, SIGNAL(signalSendToClient(QByteArray)),
            socketIo, SLOT(onSendToClient(QByteArray)));

    //connect(thread, SIGNAL(started()), socketIo, SLOT(socketInit()));
    socketIo->socketInit();
    //thread->start();
}

int processingThread::getKey()
{
    return key;
}

void processingThread::onStateChanged(QAbstractSocket::SocketState socketState)
{
    qDebug() << socketState;
    switch (socketState) {
    case QTcpSocket::ConnectedState:
        break;
    case QTcpSocket::UnconnectedState:
        emit signalDisconnect(key);
        break;
    default:
        break;
    }
}

void processingThread::onSendUpdateRequest()
{

    qDebug() << "onSendUpdateRequest()---------------------------" << QThread::currentThreadId();
        if (fileList.isEmpty()) {
            fileList = getDirAllFile(updatePath);
        }

        TcpData tData;
        FileData fData;
        tData.type = Type::UPDATE;
        QFile f ("./updateContent.txt");
        if (f.open(QIODevice::ReadOnly)) {
            QByteArray data = f.readAll();
            qDebug() << data.data();
            fData.fileContent = data;
            fData.fileName = QString(fileList.size()); //此处吧文件数量发送出去，客户端更新界面进度
            tData.data = convert(fData);
            emit signalSendToClient(envelop(tData));
        }
        else {
            qDebug() << "file open fail";
        }
}

void processingThread::onReadyRead(QByteArray data)
{
    sourceData.append(data);
    TcpData tData = unpack(sourceData);
    /*拆包后如果正常拆出进入循环发送数据后继续拆包，为了防止粘包处理*/
    while (Type::INVALID != tData.type) {
        datProcessing(tData);
        tData = unpack(sourceData);
    }

}

void processingThread::onDisconnect(int num)
{
    qDebug() << "-----";
    emit signalDisconnect(num);
}

QByteArray processingThread::envelop(TcpData tData)
{
    QByteArray ret;
    QDataStream ds(&ret, QIODevice::WriteOnly);
    //3+4+4+2=13
    ds << tData.type << tData.data.size()
       << qChecksum(tData.data, tData.data.size());
    ret.prepend("$$$");
    ret.append(tData.data);
    return ret;
}

template<typename T>
QByteArray processingThread::convertToByte(T v)
{
    QByteArray ret;
    for (size_t i = 0; i < sizeof(T); i++) {
        ret.prepend(char(v >> (i * 8)));
    }

    return ret;
}

QByteArray processingThread::convert(FileData fd)
{
    QByteArray data;
    data.append(convertToByte(fd.MD5.size()));
    data.append(fd.MD5);
    data.append((convertToByte(QByteArray().append(fd.fileName).size())));
    data.append(fd.fileName);
    data.append(fd.fileContent);
    return data;
}

FileData processingThread::convert(QByteArray data)
{
    FileData fData;
    if (data.size() < 8) {
        return fData;
    }
    qint32 ms = data.left(4).toHex().toInt(0, 16);
    data.remove(0, 4);
    fData.MD5 = data.left(ms);
    data.remove(0, ms);
    qint32 ns = data.left(4).toHex().toInt(0, 16);
    data.remove(0, 4);
    fData.fileName = QString::fromUtf8(data.left(ns));
    data.remove(0, ns);
    fData.fileContent = data;

    return fData;
}

TcpData processingThread::unpack(QByteArray &data)
{
    TcpData tDAta;
    tDAta.type = Type::INVALID;
    while ((data.size() > 3) && !data.startsWith("$$$")) {
        data.remove(0, 1);
    }

    if (data.size() < 13) {
        return tDAta;
    }
    /*cs获取封包的TcpData::data 的byte大小*/
    qint32 cs = data.mid(7, 4).toHex().toInt(0, 16);

    if (cs < 1) {
        data.remove(0, 3);
        return tDAta;
    }
    /*如果总包大小 小于 13+cs数据错误*/
    if (data.size() < (13 + cs)) {
        return tDAta;
    }

    data.remove(0, 3);
    QDataStream ds(&data, QIODevice::ReadOnly);
    quint16 checkNum = 0;

    int type;
    ds >> type >> cs >> checkNum;
    tDAta.type = Type(type);

    //4+4+2
    data.remove(0, 10); //删除type和type大小和校验
    /*数据赋值，删除*/
    tDAta.data = data.left(cs);
    data.remove(0, cs);

    //数据校验判断
    if (checkNum != qChecksum(tDAta.data, tDAta.data.size())) {
        tDAta.type = Type::INVALID;
        tDAta.data.clear();
        return tDAta;
    }

    return tDAta;
}

const QString processingThread::countMd5(const QString filePath)
{
    if (QFileInfo(filePath).exists()) {
        QFile f(filePath);
        QByteArray fileData;
        if (f.open(QIODevice::ReadOnly)) {
            fileData = f.readAll();
            f.close();
        }
        QCryptographicHash cgh(QCryptographicHash::Md5);
        cgh.addData(fileData);
        return cgh.result().toHex();
    }
    return "";
}

const QStringList processingThread::getDirAllFile(QString path)
{
    if (!path.endsWith('/')) {
        path.append('/');
    }
    QDir dir(path);
    QStringList files;

    foreach (QString name, dir.entryList(QDir::AllEntries | QDir::NoDotAndDotDot)) {
        QString newPath = path + name;
        if (!QFileInfo(newPath).isSymLink() && QFileInfo(newPath).isDir()) {
            files.append(getDirAllFile(newPath));
        }
        //此处特殊需求过滤文件夹
        if (!QFileInfo(newPath).isDir()) {
           files.append(newPath);
        }
    }
    return files;
}

void processingThread::startUpdate()
{
    if (fileList.isEmpty()) {
        return;
    }
    qDebug() << fileList;
    TcpData tData;
    tData.type = Type::START;

    FileData fData;
    QString temp = fileList.at(0);
    fData.fileName = temp.remove(updatePath);
    qDebug() << fData.fileName << "------------------------------";
    fData.MD5 = countMd5(fileList.at(0));

    /*结构体转换*/
    tData.data = convert(fData);
    emit signalSendToClient(envelop(tData));
}

void processingThread::startTransfer(QString fileName)
{
    //此处可优化，文件信息改为结构体存储，省略以下操作
    if (fileName != "") {
        //此处操作是文件重传可优化，枚举加error接受判断根据error来重传。
        for (int i = 0; i < fileList.size(); ++i) {
            if (QFileInfo(fileList.at(i)).fileName() == fileName) {
                fileList.move(i, 0);
            }
        }
    }

    if (fileList.isEmpty()) {
        return;
    }

    QFile f(fileList.at(0));
    if (f.open(QIODevice::ReadOnly)) {
        qint64 pos = 0; //读取的位置
        TcpData tData;
        FileData fData;

        tData.type = Type::DATA;
        QString temp = fileList.at(0);
        fData.fileName = temp.remove(updatePath);
        //send File Data
        while (pos < f.size()) {
            QThread::msleep(10);

            fData.fileContent = f.read(byteSize);
            pos += fData.fileContent.size(); //读取位置每次加读取大小
            tData.data = convert(fData);

            emit signalSendToClient(envelop(tData));
        }
        sendFinish();
    }
    else {

    }

}

void processingThread::finish(FileData tData)
{
    qDebug() << "finish" << fileList.size();
    if (fileList.isEmpty()) {
        //all File Transfer Finish

    }
    else {
        for (int i = 0; i < fileList.size(); ++i) {
            QString temp = fileList.at(i);
            if (temp.remove(updatePath) == tData.fileName) {
                fileList.removeAt(i);
                startUpdate();
                break;
            }
        }
    }
}

void processingThread::sendFinish()
{
    //send File Finish
    if (fileList.isEmpty()) {
        return;
    }
    TcpData tData;
    FileData fData;
    tData.type = Type::FINISH;
    fData.fileContent.clear();
    fData.MD5 = countMd5(fileList.at(0));
    QString temp = fileList.at(0);
    fData.fileName = temp.remove(updatePath);
    tData.data = convert(fData);
    emit signalSendToClient(envelop(tData));
}

void processingThread::versionsExamine(FileData tData)
{
    QJsonObject jo = QJsonDocument::fromJson(tData.fileContent).object();
    qDebug() << jo;
    onSendUpdateRequest();
}

void processingThread::datProcessing(TcpData tData)
{
    FileData fData = convert(tData.data);
    switch (tData.type) {
    case Type::UPDATE:{
        startUpdate();
    }
        break;
    case Type::START:{
        startTransfer(fData.fileName);
    }
        break;
    case Type::FINISH:{
        finish(convert(tData.data));
    }
        break;
    case Type::VERSIONS_EXAMINE:{
        versionsExamine(convert(tData.data));
    }
    default:
        break;
    }
}
